# 生成器 ()

"""
通过列表生成式（列表推导式），我们可以直接创建一个列表:
    但是，收到内容容量限制，列表容量肯定是有限的。而且创建一个包含100万元素的列表，不仅占用很大的内存空间。
    如果我们仅仅需要访问前面几个元素，那后面绝大多数元素占用的空间都白白浪费了。
    所以，如果列表元素可以按照某种算法推算出来，那我们是否可以在循环的过程中不断推算出后续的元素呢？
    这样就不必创建完整的list，从而节省大量的空间，在Python中，这种一边循环一边计算的机制，成为生成器：generator
得到生成器的方式：
    1、列表推导式得到生成器：
        g = (x+1 for x in range(10))
    2、借助函数完成
        (1) 定义一个函数，函数中使用yield关键字
                def gen():
                    ...
                    yield
                g = gen()
        (2) 调用函数，接受调用的结果
        (3) 得到的结果就是生成器
        (4) 借助next() 或者__next__()获取
    3、产生元素：
        (1) next(generator) ----->每次调用都会产生一个新的元素，如果元素产生完毕再次调用的化就会产生异常
        (2) 成器方法：
            g.__next__()
            g.send(value)
    4、应用领域：协程
注意：
    只要函数中出现yield（生成）关键字，说明函数就不是函数拉，变成生成器了

生成器方法：
    __next__() :  获取下一个元素
    send(value): 每次生成器调用中传值的；注意第一次调用传递一个send(None)


"""
# 牺牲时间换空间
# x = [x for x in range(1000000000000000000000000000000000000)]

new_lst = [x * 3 for x in range(10)]
print(new_lst)

# 生成器()

g_lst = (x * 3 for x in range(10))  # 最多10个超过，就报错:StopIteration
print(type(g_lst))  # <class 'generator'>
print(g_lst)
print(g_lst.__next__())
# 没调用一次产生一个元素
print(next(g_lst))

while True:
    try:
        print(next(g_lst))
    except StopIteration:
        print("元素已经取完!")
        break


# 测试函数生成器


def func():
    n = 0
    while True:
        n += 1
        print(n)
        yield n  # 表示是生成器


x = func()  # <generator object func at 0x000002440E14EAC8>
print(x)
print(next(x))

print("斐波那契数列".center(30, '*'))


# 斐波那契数列 n ,m , n+m

def fibonacci():
    a, b = 0, 1
    n = 0
    while True:
        print(b)
        yield b
        a, b = b, a + b
        n += 1


m = 10
y = fibonacci()

while m > 0:
    next(y)
    m -= 1
print("yield".center(30, '#'))


def tep():
    i = 0
    while i < 5:
        temp = yield i
        print('temp', temp)
        for x in range(temp):
            print("------------------", x)
        print("***************")
        i += 1
    return '没有更多的数据'


g = tep()
# print(next(g))
# print(next(g))
# print(next(g))

# g.__next__()
n = g.send(None)
print("n", n)
n1 = g.send(3)  # TypeError: can't send non-None value to a just-started generator
print("n1", n1)
n2 = g.send(5)
print("n2", n2)
